package io.github.bloquesoft.entity.storage.analysis;

import io.github.bloquesoft.entity.value.EntityValue;
import io.github.bloquesoft.entity.value.ListPropertyValue;
import io.github.bloquesoft.entity.value.PojoPropertyValue;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class EntityAnalysis
{
    public EntityAnalysisResult analyze(EntityValue sourceEntityValue, EntityValue targetEntityValue)
    {
        EntityAnalysisResult result = new EntityAnalysisResult(targetEntityValue.getEntityDefinition());
        if (sourceEntityValue == null || sourceEntityValue.isNullValue()) {
            this.analyzeInsert(targetEntityValue, result);
        }
        else {
            this.analyzeUpdate(sourceEntityValue, targetEntityValue, result);
        }
        return result;
    }

    private void analyzeInsert(EntityValue targetEntityValue, EntityAnalysisResult result)
    {
        result.addInsert(targetEntityValue);
        List<ListPropertyValue> listPropertyValueList =  targetEntityValue.getListPropertyValueList();
        for(ListPropertyValue listPropertyValue : listPropertyValueList)
        {
            List<EntityValue> valueList = listPropertyValue.getItemList();
            if (valueList != null)
            {
                for(EntityValue entityValue : valueList)
                {
                    analyzeInsert(entityValue, result);
                }

            }
        }
    }

    private void analyzeUpdate(EntityValue sourceEntityValue, EntityValue targetEntityValue, EntityAnalysisResult result)
    {
        if (!isPrimitivePropertyValueEqual(sourceEntityValue, targetEntityValue))
        {
            result.addUpdate(targetEntityValue);
        }

        List<ListPropertyValue> targetListPropertyValueList =  targetEntityValue.getListPropertyValueList();
        for(ListPropertyValue targetListPropertyValue : targetListPropertyValueList)
        {
            ListPropertyValue sourceListPropertyValue = sourceEntityValue.getListPropertyValue(targetListPropertyValue.getPropertyId());
            if (sourceListPropertyValue == null || sourceListPropertyValue.getItemList() == null) {
                targetListPropertyValue.getItemList().forEach(i-> this.analyzeInsert(i, result));
            }
            else {
                this.compare(sourceListPropertyValue, targetListPropertyValue, result);
            }
        }
    }

    private void analyzeDelete(EntityValue sourceEntityValue, EntityAnalysisResult result)
    {
        List<ListPropertyValue> listPropertyValueList =  sourceEntityValue.getListPropertyValueList();
        for(ListPropertyValue listPropertyValue : listPropertyValueList)
        {
            for(EntityValue entityValue : listPropertyValue.getItemList())
            {
                analyzeDelete(entityValue, result);
            }
        }
        result.addDelete(sourceEntityValue);
    }

    private void compare(ListPropertyValue sourceListValue, ListPropertyValue targetListValue, EntityAnalysisResult result)
    {

        List<Integer> sourceNotUsedIndexList = IntStream.range(0, sourceListValue.getItemList().size()).boxed().collect(Collectors.toList());

        for(EntityValue targetItem : targetListValue.getItemList())
        {
            int i= 0;
            int selectedSourceIndex = -1;
            while(i < sourceNotUsedIndexList.size())
            {
                int index = sourceNotUsedIndexList.get(i);
                EntityValue sourceItem = sourceListValue.getItemList().get(index);
                if (isSamePrimaryKeyValue(sourceItem, targetItem))
                {
                    analyzeUpdate(sourceItem, targetItem, result);
                    selectedSourceIndex = i;
                    break;
                }
                i++;
            }
            if (selectedSourceIndex == -1) {
                analyzeInsert(targetItem, result);
            }
            else {
                sourceNotUsedIndexList.remove(selectedSourceIndex);
            }
        }

        for(Integer index : sourceNotUsedIndexList)
        {
            EntityValue sourceItem = sourceListValue.getItemList().get(index);
            this.analyzeDelete(sourceItem, result);
        }
    }

    private boolean isSamePrimaryKeyValue(EntityValue sourceEntityValue, EntityValue targetEntityValue)
    {
        return sourceEntityValue.getPrimaryKeyValue().getValue().equals(targetEntityValue.getPrimaryKeyValue().getValue());
    }

    private boolean isPrimitivePropertyValueEqual(EntityValue sourceEntityValue, EntityValue targetEntityValue)
    {
        for(PojoPropertyValue targetPropertyValue : targetEntityValue.getPojoPropertyValueList())
        {
            String propertyId = targetPropertyValue.getPropertyId();
            PojoPropertyValue sourcePropertyValue = sourceEntityValue.getPojoPropertyValue(propertyId);
            if (!isPrimitivePropertyValueEqual(sourcePropertyValue, targetPropertyValue))
            {
                return false;
            }
        }
        return true;
    }

    private boolean isPrimitivePropertyValueEqual(PojoPropertyValue sourceValue, PojoPropertyValue targetValue) {
        Object sourceObject = sourceValue.getValue();
        Object targetObject = targetValue.getValue();
        if (sourceObject != null)
        {
            return sourceObject.equals(targetObject);
        }
        else {
            return targetObject == null;
        }
    }
}